//
//  ATTimer.m
//  NSTimerDemo
//
//  Created by lianglibao on 2018/11/13.
//  Copyright © 2018年 lianglibao. All rights reserved.
//

#import "ATTimer.h"

#define QH_IS(obj, cls)                                 (obj != nil && [obj isKindOfClass:[cls class]])
#define QH_IS_STRING(obj)                               (QH_IS(obj, NSString))
#define QH_IS_VALID_STRING(obj)                       (QH_IS_STRING(obj) && [obj length])
#define QHLogError(...) NSLog(__VA_ARGS__);

static NSString *const kATTimerTotalSecond = @"kATTimerTotalSecond";
static NSString *const kATTimerCountdownSecond = @"kATTimerCountdownSecond";
static NSString *const kATTimerBeginCountdownNumber = @"kATTimerBeginCountdownNumber";
static NSString *const kATTimerBeginCountingUnit = @"kATTimerBegincountingUnit";
static NSString *const kATTimerBeginCountdownBlock = @"kATTimerBeginCountdownBlock";
static NSString *const kATTimerName = @"kATTimerName";

@interface ATTimerProxy : NSProxy
@property (nonatomic, weak) id target;
@property (nonatomic, weak) NSString *timerName;
@end

@implementation ATTimerProxy : NSProxy
/*
 * NSProxy 一个专业处理消息转发的class,与NSObject同级别独立根类
 * NSProxy内部weak持有被代理的对象target
 * NSProxy对象成为NSTimer对象的traget,当NSProxy对象没有实现timerAction方法时,
                                    不会进入动态方法解析阶段    resolveClassMethod
                                    也不会进入快速消息转发阶段  forwardingTargetForSelector
                                    直接进入最后打消息转发(拯救)阶段 methodSignatureForSelector 和 forwardInvocation
 *
 */
+ (instancetype)proxyWithTagret:(id)target timerName:(NSString *)timerName {
    ATTimerProxy *proxy = [ATTimerProxy alloc];
    proxy.target = target;
    proxy.timerName = timerName;
    return proxy;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    if (self.target) {
        return [self.target methodSignatureForSelector:sel];
    } else {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    if (self.target) {
        [invocation invokeWithTarget:self.target];
    } else {
        [[ATTimer sharedInstance] stopTimerWithName:self.timerName];
    }
}

@end

@interface ATTimer()
@property (nonatomic, strong) NSMutableDictionary<NSString*, NSTimer*> *timers;

@end


@implementation ATTimer

+ (instancetype)sharedInstance {
    static ATTimer *_sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

// ATTimer管理策略 >>>> ================================================================================== //
/**
 * 全新的timer管理策略,使用全局字典记录!!
 */
- (NSMutableDictionary<NSString *,NSTimer *> *)timers {
    if (!_timers) {
        _timers = [NSMutableDictionary dictionaryWithCapacity:1];
    }
    return _timers;
}

// >>> end ==================================================================================================== //


- (void)setupTimerWithName:(NSString * _Nonnull)name
              timeInterval:(NSTimeInterval)timeInterval
                    target:(id _Nonnull)aTarget
                  selector:(SEL _Nonnull)aSelector
                   repeats:(BOOL)yesOrNo {
    if (!QH_IS_VALID_STRING(name)) {
        QHLogError(@"🐱🐱🐱:需要新建的timer名称不能是空串!!!");
        return;
    }
    
    NSTimer *existTimer = [[self timers] valueForKey:name];
    // 1.已存在&&运行中
    if (existTimer && [existTimer isValid]) {
        QHLogError(@"🐱🐱🐱:需要新建的timer已经在运行中!!!");
        return;
        // 2.已存在
    } else if (existTimer && [[existTimer userInfo] isEqualToString:name]) {
        // 停止&释放现有的,创建新的? 返回消息,什么都不做?
        
        // 3.不存在,重新创建
    } else {
        existTimer = [NSTimer
                      scheduledTimerWithTimeInterval:(timeInterval ? timeInterval : 1.0f)
                      target:[ATTimerProxy proxyWithTagret:aTarget timerName:name]
                      selector:aSelector
                      userInfo:name
                      repeats:yesOrNo];
        [self.timers setObject:existTimer forKey:name];
    }
}

- (void)stopTimerWithName:(NSString * _Nonnull)name {
    if (!QH_IS_VALID_STRING(name)) {
        QHLogError(@"🐱🐱🐱:需要停止的timer名称不能是空串!!!");
        return;
    }
    NSTimer *existTimer = [[self timers] valueForKey:name];
    if (existTimer) {
        [existTimer invalidate];
        existTimer = nil;
        [self.timers removeObjectForKey:name];
    } else {
        QHLogError(@"🐱🐱🐱:需要停止的timer:%@已停止或不存在!!!", name);
    }
}

- (void)setupCountdownTimerWithName:(NSString * _Nonnull)name
                        totalSecond:(NSInteger)totalSecond
                    countdownSecond:(NSInteger)countdownSecond
                     countdownBlock:(ATTimerPlayCompletionBlock _Nullable)countdownBlock {
    
    if (!QH_IS_VALID_STRING(name)) {
        QHLogError(@"🐱🐱🐱:需要新建的timer名称不能是空串!!!");
        return;
    }
    
    NSTimer *existTimer = [[self timers] valueForKey:name];
    
    if (existTimer && [existTimer isValid]) {
        // 1.已存在&&运行中
        QHLogError(@"🐱🐱🐱:需要新建的timer已经在运行中!!!");
        return;
        
    } else if (existTimer && [[existTimer userInfo] isEqualToString:name]) {
        // 2.已存在
        // 停止&释放现有的,创建新的? 返回消息,什么都不做?
        
    } else {
        // 3.不存在,重新创建
        NSInteger beginCountdownNumber = 0;
        if (totalSecond >= countdownSecond) {
            beginCountdownNumber = totalSecond - countdownSecond;
        }
        NSInteger countingUnit = 0;
        
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity:5];
        
        [userInfo setObject:@(totalSecond)  forKey:kATTimerTotalSecond];
        [userInfo setObject:@(countdownSecond) forKey:kATTimerCountdownSecond];
        [userInfo setObject:@(beginCountdownNumber) forKey:kATTimerBeginCountdownNumber];
        [userInfo setObject:@(countingUnit) forKey:kATTimerBeginCountingUnit];
        if (countdownBlock) {
            [userInfo setObject:countdownBlock forKey:kATTimerBeginCountdownBlock];
        }
        [userInfo setObject:name forKey:kATTimerName];
        
        existTimer = [NSTimer
                      scheduledTimerWithTimeInterval:1.0f
                      target:self
                      selector:@selector(updateAction:)
                      userInfo:userInfo
                      repeats:YES];
        [self.timers setObject:existTimer forKey:name];
    }
}

- (void)updateAction:(NSTimer *)timer {
    NSMutableDictionary *userInfo = timer.userInfo;
    NSInteger countingUnit = [[userInfo valueForKey:kATTimerBeginCountingUnit] integerValue];
    NSInteger totalSecond = [[userInfo valueForKey:kATTimerTotalSecond] integerValue];
    NSInteger beginCountdownNumber = [[userInfo valueForKey:kATTimerBeginCountdownNumber] integerValue];
    NSString *name = [userInfo valueForKey:kATTimerName];
    
    // 执行到总计时时结束定时器
    if (countingUnit == totalSecond) {
        [self stopTimerWithName:name];
    }
    
    if (countingUnit >= beginCountdownNumber) {
        ATTimerPlayCompletionBlock block = [userInfo valueForKey:kATTimerBeginCountdownBlock];
        // 回调外面执行操作
        if (block) {
            NSInteger countdownSecond = [[userInfo valueForKey:kATTimerCountdownSecond] integerValue];
            if (countdownSecond < 0) {
                return;
            }
            
            block(countdownSecond);
            countdownSecond -= 1;
            [userInfo setObject:@(countdownSecond) forKey:kATTimerCountdownSecond];
        }
    }
    
    // 累加时间计时
    countingUnit += 1;
    [userInfo setObject:@(countingUnit) forKey:kATTimerBeginCountingUnit];
}


///<<<<============================更好的解决方案======================
/*
 * 1.解决NSTimer由于runloop处理source任务繁重而导致不准时
 * 2.封装性更强,解决NSTimer对targets的循环引用
 * 3.增加多线程处理timerAction
 */
static NSMutableDictionary *timers_;
dispatch_semaphore_t semaphore_;
+ (void)initialize
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        timers_ = [NSMutableDictionary dictionary];
        semaphore_ = dispatch_semaphore_create(1);
    });
}

+ (void)stopTimerWithName:(NSString * _Nonnull)name {
    if (!QH_IS_VALID_STRING(name)) {
        QHLogError(@"🐱🐱🐱:需要停止的timer名称不能是空串!!!");
        return;
    }
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    if ([timers_ valueForKey:name]) {
        [self cancelTask:name];
    }
    dispatch_semaphore_signal(semaphore_);
}

+ (NSString *_Nullable)setupCountdownTimerWithTotalSecond:(NSInteger)totalSecond
                                          countdownSecond:(NSInteger)countdownSecond
                                           countdownBlock:(ATTimerPlayCompletionBlock _Nullable)countdownBlock {
    return [self setupCountdownTimerWithTotalSecond:totalSecond
                                    countdownSecond:countdownSecond
                                           interval:1
                                     countdownBlock:countdownBlock
                                              async:NO];
}

+ (NSString *)setupCountdownTimerWithTotalSecond:(NSInteger)totalSecond
                                 countdownSecond:(NSInteger)countdownSecond
                                        interval:(NSInteger)interval
                                  countdownBlock:(ATTimerPlayCompletionBlock _Nullable)countdownBlock
                                           async:(BOOL)async {
    if (!countdownBlock || (totalSecond < countdownSecond) || (totalSecond <=0) || (countdownSecond < 0)) {
        return nil;
    }
    
    // 队列
    dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
    
    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置时间
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, (totalSecond - countdownSecond) * NSEC_PER_SEC),
                              interval * NSEC_PER_SEC, 0);
    
    
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 定时器的唯一标识
    NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
    // 存放到字典中
    timers_[name] = timer;
    
    dispatch_semaphore_signal(semaphore_);
    
    __block NSInteger countdownNum = countdownSecond;
    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
        if (countdownNum == 0) {
            [self stopTimerWithName:name];
        }
        
        if (countdownBlock) {
            countdownBlock(countdownNum);
        }
        countdownNum--;
    });
    
    // 启动定时器
    dispatch_resume(timer);
    
    return name;
}

+ (NSString *)execTimerTask:(void (^)(void))task
                      start:(NSTimeInterval)start
                   interval:(NSTimeInterval)interval
                    repeats:(BOOL)repeats
                      async:(BOOL)async {
    if (!task || start < 0 || (interval <= 0 && repeats)) return nil;
    
    // 队列
    dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
    
    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 设置时间
    dispatch_source_set_timer(timer,
                              dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC),
                              interval * NSEC_PER_SEC, 0);
    
    
    dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
    // 定时器的唯一标识
    NSString *name = [NSString stringWithFormat:@"%zd", timers_.count];
    // 存放到字典中
    timers_[name] = timer;
    
    dispatch_semaphore_signal(semaphore_);
    
    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
        task();
        
        if (!repeats) { // 不重复的任务
            [self cancelTask:name];
        }
    });
    
    // 启动定时器
    dispatch_resume(timer);
    
    return name;
}

+ (NSString *)execTimerTaskTarget:(id)target
                         selector:(SEL)selector
                            start:(NSTimeInterval)start
                         interval:(NSTimeInterval)interval
                          repeats:(BOOL)repeats
                            async:(BOOL)async {
    if (!target || !selector) return nil;
    
    return [self execTimerTask:^{
        if ([target respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            [target performSelector:selector];
#pragma clang diagnostic pop
        }
    } start:start interval:interval repeats:repeats async:async];
}

+ (void)cancelTask:(NSString *)name {
    if (name.length == 0) return;
    
    dispatch_source_t timer = timers_[name];
    if (timer) {
        dispatch_source_cancel(timer);
        [timers_ removeObjectForKey:name];
    }
}

@end
